package com.menggudf.dao.base;


import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Selection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.CollectionUtils;

public class BaseDaoImpl<T>
  implements BaseDao<T>
{
  private static final Log log = LogFactory.getLog(BaseDaoImpl.class);
  private int batchSize = 50;
  @PersistenceContext(unitName="medicalUnit")
  protected EntityManager entityManager;
  
  public void setBatchSize(int batchSize)
  {
    this.batchSize = batchSize;
  }
  
  public EntityManager getEntityManager()
  {
    return this.entityManager;
  }
  
  public CriteriaBuilder getCriteriaBuilder()
  {
    return this.entityManager.getCriteriaBuilder();
  }
  
  public T insert(T entity)
  {
    try
    {
      this.entityManager.persist(entity);
    }
    catch (RuntimeException re)
    {
      log.error("insert failed", re);
      throw re;
    }
    return entity;
  }
  
  public List<T> insertList(List<T> entityList)
  {
    List<T> entityList2 = new ArrayList();
    if (entityList != null)
    {
      int i = 0;
      for (T t : entityList)
      {
        T t2 = insert(t);
        entityList2.add(t2);
        if (i % this.batchSize == 0) {
          this.entityManager.flush();
        }
        i++;
      }
      log.debug(entityList.get(0).getClass() + "������������" + i + "��");
    }
    return entityList2.size() == 0 ? entityList : entityList2;
  }
  
  public T update(T entity)
  {
    try
    {
      return (T)this.entityManager.merge(entity);
    }
    catch (RuntimeException re)
    {
      log.error("update failed", re);
      throw re;
    }
  }
  
  public List<T> updateList(List<T> entityList)
  {
    List<T> entityList2 = new ArrayList();
    if (entityList != null)
    {
      int i = 0;
      for (T t : entityList)
      {
        T t2 = update(t);
        entityList2.add(t2);
        if (i % this.batchSize == 0) {
          this.entityManager.flush();
        }
        i++;
      }
      log.debug(entityList.get(0).getClass() + "������������" + i + "��");
    }
    return entityList2.size() == 0 ? entityList : entityList2;
  }
  
  public void delete(BaseEntity entity)
  {
    try
    {
      this.entityManager.remove(entity);
    }
    catch (RuntimeException re)
    {
      log.error("delete failed", re);
      throw re;
    }
  }
  
  public void delete(Collection<? extends BaseEntity> entityList)
  {
    if (entityList != null) {
      for (BaseEntity entity : entityList) {
        delete(entity);
      }
    }
  }
  
  public T findById(Class<T> clazz, Integer id)
  {
    try
    {
      return (T)this.entityManager.find(clazz, id);
    }
    catch (RuntimeException re)
    {
      log.error("findById failed", re);
    }
    return null;
  }
  
  public List<T> findAll(Class<T> clazz)
  {
    try
    {
      return 
        this.entityManager.createQuery("from " + clazz.getSimpleName(), clazz).getResultList();
    }
    catch (RuntimeException re)
    {
      log.error("findAll failed", re);
    }
    return null;
  }
  
  public List<T> findByIds(Class<T> clazz, List<Integer> ids)
  {
    return 
    
      this.entityManager.createQuery("from " + clazz.getSimpleName() + " a where a.id in ?1 ", clazz).setParameter(1, ids).getResultList();
  }
  
  public T findBy(Class<T> clazz, String fieldName, String fieldValue)
  {
    List<T> list = findListBy(clazz, fieldName, fieldValue);
    if ((list != null) && (list.size() > 0)) {
      return (T)list.get(0);
    }
    return null;
  }
  
  public List<T> findListBy(Class<T> clazz, String fieldName, String fieldValue)
  {
    return 
    
      this.entityManager.createQuery("from " + clazz.getSimpleName() + " where " + fieldName + "=?1", clazz).setParameter(1, fieldValue).getResultList();
  }
  
  public List<Integer> getIds(List<T> entityList)
  {
    List<Integer> idList = new ArrayList();
    if (!CollectionUtils.isEmpty(entityList)) {
      for (T entity : entityList) {
        if ((entity instanceof BaseEntity)) {
          idList.add(Integer.valueOf(((BaseEntity)entity).getId()));
        }
      }
    }
    return idList;
  }
  
  public boolean contains(Class<T> clazz, String fieldName, String fieldValue)
  {
    try
    {
      List<T> list = findListBy(clazz, fieldName, fieldValue);
      if ((list != null) && (list.size() > 0)) {
        return true;
      }
    }
    catch (Exception e)
    {
      log.error("contains failed", e);
    }
    return false;
  }
  
  public T findBy(Class<T> clazz, Map<String, Object> paramMap)
  {
    List<T> list = findListBy(clazz, paramMap);
    if ((list != null) && (list.size() > 0)) {
      return (T)list.get(0);
    }
    return null;
  }
  
  public List<T> findListBy(Class<T> clazz, Map<String, Object> paramMap)
  {
    if ((paramMap != null) && (!paramMap.isEmpty()))
    {
      BaseQuery<T> baseQuery = new BaseQuery(clazz, getEntityManager());
      for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
        baseQuery.eq((String)entry.getKey(), entry.getValue());
      }
      return 
        this.entityManager.createQuery(baseQuery.getCriteriaQuery()).getResultList();
    }
    return null;
  }
  
  public List<T> findByCriteriaQuery(CriteriaQuery<T> criteriaQuery)
  {
    try
    {
      return this.entityManager.createQuery(criteriaQuery).getResultList();
    }
    catch (RuntimeException re)
    {
      log.error("findByCriteriaQuery failed", re);
      throw re;
    }
  }
  
  public List<T> findByBaseQuery(BaseQuery<T> baseQuery)
  {
    return 
      this.entityManager.createQuery(baseQuery.getCriteriaQuery()).getResultList();
  }
  
  public Page<T> listPage(Class<T> clazz, BaseQuery<T> baseQuery, int pageSize, int offset)
  {
    return new Page(
      this.entityManager.createQuery(baseQuery.getCriteriaQuery())
      .setFirstResult(offset).setMaxResults(pageSize)
      .getResultList(), getCount(clazz, baseQuery).longValue(), pageSize, 
      offset);
  }
  
  protected Long getCount(Class<T> clazz, BaseQuery<T> baseQuery)
  {
    Selection<T> selection = baseQuery.getCriteriaQuery().getSelection();
    baseQuery.getCriteriaQuery().select(selection);
    baseQuery.getCriteriaQuery().select(
            (Selection<? extends T>) baseQuery.getCriteriaBuilder().count(
            baseQuery.getFrom()));
    Long count = 
    
      (Long)getEntityManager().createQuery(baseQuery.getCriteriaQuery()).getResultList().get(0);
    return count;
  }
  
  public Page<T> listPage(Class<T> clazz, int pageSize, int offset)
  {
    return new Page(
      this.entityManager
      .createQuery("from " + clazz.getSimpleName(), clazz)
      .setFirstResult(offset).setMaxResults(pageSize)
      .getResultList(), getCount(clazz).longValue(), pageSize, offset);
  }
  
  protected Long getCount(Class<T> clazz)
  {
    return (Long)this.entityManager.createQuery(
      "select count(1) from " + clazz.getSimpleName())
      .getSingleResult();
  }
  
  public List<T> list(BaseQuery<T> baseQuery)
  {
    return this.entityManager.createQuery(baseQuery.getCriteriaQuery()).getResultList();
  }
  
  public T findBy(Class<T> clazz, String fieldName, Object fieldValue)
  {
    List<T> list = findListBy(clazz, fieldName, fieldValue);
    if ((list != null) && (list.size() > 0)) {
      return (T)list.get(0);
    }
    return null;
  }
  
  public List<T> findListBy(Class<T> clazz, String fieldName, Object fieldValue)
  {
    return 
    
      this.entityManager.createQuery("from " + clazz.getSimpleName() + " where " + fieldName + "=?1", clazz).setParameter(1, fieldValue).getResultList();
  }
}
